#!/usr/local/bin/python3
# *_* coding: UTF-8 *_*
# @IDE: PyCharm
# @Version: Python3.97
# @Author: Kendrick.Kc
# @Email: 509556985@qq.com
# @File: extract.py
# @Inst: 提取交易对
# @Time: 2022/8/30 23:18
# -----


import math

from binance_dgs.set_dgs import SetDGSGrid
from utils.timestamp import now_time_stamp, next_time_stamp


class ExtractSymbol:
    """
    计算DGS、杠杆
    """

    def __init__(self, binance, symbol):
        self.binance = binance
        self.symbol = symbol
        self.set_dgs_grid = SetDGSGrid(binance, symbol)

    def extract(self):

        # 处理最小的dgs数据的交易对
        res_min_dgs = self.binance.cache_get(self.binance.parameter.db_coll_min_dgs) or []
        if res_min_dgs:

            # 更新过期交易对
            now_min_dgs_list = []
            for item in res_min_dgs:
                if now_time_stamp() < item["timestamp"]:
                    now_min_dgs_list.append(item)

            if now_min_dgs_list:
                self.binance.cache_set(self.binance.parameter.db_coll_min_dgs, now_min_dgs_list)

                # 如果当前min_dgs表中存在则返回空，过滤掉该交易对
                for i in range(len(now_min_dgs_list)):
                    if now_min_dgs_list[i]["symbol"] == self.symbol:
                        self.binance.logger_info(f"{self.symbol}在{self.binance.parameter.db_coll_min_dgs}中存在，过滤该交易对")
                        return None

        res_grid = self.set_dgs_grid.json_write()

        # dgs少于下面这个数则抛弃该交易对(包含极速)
        if res_grid['info']['grid_number'] < self.binance.parameter.min_dgs_number:
            # 将交易对记录到数据库
            res_min_dgs.append({"timestamp": next_time_stamp(self.binance, self.symbol), "symbol": self.symbol})
            self.binance.cache_set(self.binance.parameter.db_coll_min_dgs, res_min_dgs)
            self.binance.logger_info(f"{self.symbol}交易对的{self.binance.strategy_name}数小于{self.binance.parameter.min_dgs_number}, 故此放弃此交易对.")
            return None

        dgs = self.leverage_or_amount(res_grid)

        return self.is_pos(dgs)

    def leverage_or_amount(self, dgs):
        """
        计算下单量、杠杆
        :param dgs: DGS数据
        :return: dgs/None
        """

        res_max_leverage = self.set_dgs_grid.dgs_leverage_brackets()
        balance_db = self.binance.cache_get(self.binance.parameter.db_coll_balance)

        # 判断账户余额是否足以启动新交易对
        if balance_db[self.binance.parameter.db_coll_balance] < self.binance.parameter.this_margin:
            self.binance.logger_info(f"账户余额{balance_db[self.binance.parameter.db_coll_balance]}U不足以启动新交易对{dgs['info']['inst_id']}."
                                     f" 要求不低于保证金{self.binance.parameter.this_margin}{self.binance.parameter.ccy}.")
            return None

        # 使用固定：如现有余额小于账户初始余额除以线程数得出的交易金额则使用现有余额计算
        # balance = int(balance_db[Parameter.db_coll_balance_init] / Parameter.trading_count_number)
        # if balance > balance_db[Parameter.db_coll_balance]:
        #     balance = balance_db[Parameter.db_coll_balance]

        # 使用复利：如现有余额小于账户初始余额除以线程数得出的交易金额则使用现有余额计算
        ts_len = self.binance.cache_get(self.binance.parameter.db_coll_trading_symbol) or []
        trading_number = self.binance.parameter.trading_count_number - len(ts_len)
        if not ts_len:
            balance = int(balance_db[self.binance.parameter.db_coll_balance_init]) / self.binance.parameter.trading_count_number
        elif trading_number == 1:
            balance = balance_db[self.binance.parameter.db_coll_balance]
        else:
            balance = balance_db[self.binance.parameter.db_coll_balance] / trading_number

        available_balance = balance * (1 - (self.binance.parameter.this_margin / 100))  # 预留保证金后的金额
        usdt = dgs['info']['all_u']
        # 所需U数比余额少/ 加大下单量
        if available_balance >= usdt:
            multiple = math.ceil(available_balance / usdt)
            dgs = self.set_dgs_grid.json_write(amount_multiple=multiple)
            if (multiple * usdt) > available_balance:
                dgs['info']['leverage'] = math.ceil((multiple * usdt) / available_balance)

        # 所需U数比余额多/ 加大杠杆
        else:
            dgs['info']['leverage'] = math.ceil(usdt / available_balance)

        # 如果杠杆<=5时，则根据行情加大杠杆（这里解决当账户余额较多时，自动杠杆最大就是2倍的问题）
        if dgs['info']['leverage'] <= 2:
            leverage = self.balance_duoduo(int(dgs['info']['fear_greed_index']), res_max_leverage)
            self.binance.logger_info(f"{self.symbol}交易对当前杠杆为:{dgs['info']['leverage']}倍, 现根据行情重设杠杆为:{leverage}倍.")
            dgs['info']['leverage'] = leverage

        # 超出最大杠杆时
        if dgs['info']['leverage'] > res_max_leverage:
            self.binance.logger_info(f"最高杠杆{res_max_leverage}不足以启动{self.symbol}交易对.")
            return None

        dgs['info']['real_u'] = balance
        return dgs

    # 解决当账户余额较多时，自动杠杆最大就是2倍的问题
    @staticmethod
    def balance_duoduo(fgi, max_leverage, leverage_max_multiple=3):
        """
        解决当账户余额较多时，自动杠杆最大就是2倍的问题
        :param fgi: 恐慌与贪婪指数
        :param max_leverage: 最大杠杆倍数
        :param leverage_max_multiple: 恐慌与贪婪指数间距杠杆
        :return: 杠杆
        """
        leverage = int(max_leverage / (leverage_max_multiple * leverage_max_multiple))
        fgi_leverage_list = []

        for num in range(0, 101):
            if num % leverage_max_multiple == 0:

                if num <= 50:
                    leverage += 1  # fgi 越靠近 50,aa 越大
                else:
                    if leverage > 2:
                        leverage -= 1  # fgi 越远离 50,aa 越小

                for i in range(num + 1, num + leverage_max_multiple + 1):
                    fgi_leverage_list.append({
                        "fgi": i,
                        "leverage": leverage
                    })

        for item in fgi_leverage_list:
            if item["fgi"] == fgi:
                if item["leverage"] > max_leverage:
                    return max_leverage
                return item["leverage"]

    def is_pos(self, dgs):
        """
        计算是否满足线程数量再启动
        :param dgs: DGS数据
        :return: dgs/{}
        """
        if dgs is None:
            return {}

        res_trading_symbol = self.binance.cache_get(self.binance.parameter.db_coll_trading_symbol) or []
        if len(res_trading_symbol) < self.binance.parameter.trading_count_number:
            # 插入trading交易对集合
            trading_symbol = self.binance.cache_get(self.binance.parameter.db_coll_trading_symbol) or []

            # 当前trading交易对集合如何存在该symbol时则抛弃
            trading = next((item for item in trading_symbol if item['symbol'] == self.symbol), None)
            if trading:
                return {}

            trading_symbol.append({
                "symbol": self.symbol,
                "leverage": dgs['info']['leverage'],
                "all_u": dgs['info']['all_u'],
                "real_u": dgs['info']['real_u'],
                self.binance.parameter.db_coll_balance_timeStamp: now_time_stamp()
            })

            self.binance.cache_set(f"{self.binance.exchange_strategy}_{self.symbol}", dgs)
            self.binance.cache_set(self.binance.parameter.db_coll_trading_symbol, trading_symbol)
            balance = self.binance.cache_get(self.binance.parameter.db_coll_balance)
            balance[self.binance.parameter.db_coll_balance] = balance[self.binance.parameter.db_coll_balance] - dgs['info']['real_u']
            self.binance.cache_set(self.binance.parameter.db_coll_balance, balance)
            return dgs
        return {}
